﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using MongoDB.Bson;
using lo = sqCore.sqLogger;

namespace sqCore
{
    /* Docs:
    * Summary
       * Query Result object for SqlA.
       * Results organized as table.
       * Allows Save,Load,Manipulate format and compare results.
    * Variables
       * Titles - List of table headers.
       * Params - List of results rows[List<objects>].
    */
    [Serializable]
    public class sqQueryTableResult
    {
        public List<object> Titles { get; set; }
        public List<List<object>> Params { get; set; }

        /* Docs:
        * Summary: Initialize the results object.
        */
        public sqQueryTableResult(List<object> Titles, List<List<object>> Params)
        {
            this.Titles = Titles; 
            this.Params = Params; 
        }

        /* Docs:
        * Summary
           * Formats the Results to HTML format.           
        * Variables
            * AdditionalText - This text will add to the html code, before the result. 
            * TitleColor - Color of the headers background.
            * BodyColor - Color of the rows background.
        */
        public string makeHtmlMessage(string AdditionalText, string TitleColor="lightgreen", string BodyColor="lightblue")        
        {
            string htmlMessage= "<head><title></title></head><body align = center><h3 align=center>" + AdditionalText + "</h3><table align = center border =\"1\"><tr bgcolor=\""+TitleColor+"\">";
            foreach (object tab in this.Titles) //add the headers.
            {
                 htmlMessage+= "<th><font size =\"2\"><b>" + (string)tab + "</font></b></th>";
            }
            htmlMessage+="</tr>";
            foreach (List<object> row in this.Params) //add the rows.
            {
                htmlMessage+="<tr bgcolor=\""+BodyColor+"\">";
                foreach (object tab in row)
                {
                    try
                    {
                        htmlMessage += "<th><font size =\"2\"><b>" + tab.ToString() + "</font></b></th>";
                    }
                    catch (System.Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                        Console.ReadLine();
                        htmlMessage += "<th><font size =\"2\"><b></font></b></th>";
                    }
                }
                htmlMessage+="</tr>";
            }
            htmlMessage+="</table></body>";
            return htmlMessage;      
        }

        /* Docs:
        * Summary
           * Formats the Results to CSV and save them in file.
        * Variables
           * FilePath - The path to save the file.
        */
        public void makeFile(string FilePath)
        {
            string csv = "";
            foreach (object tab in this.Titles)
                csv += (string)tab+",";
            csv += "\n";

            foreach (List<object> row in this.Params)
            {
                foreach (object tab in row)
                {
                    csv += tab.ToString() + ",";
                }
                csv += "\n";
            }
            FileStream fs;
            fs = new FileStream(@FilePath,FileMode.Create,FileAccess.Write);
            StreamWriter writer = new StreamWriter(fs);
            writer.Write(csv);
            writer.Close();
            fs.Close();


        }

        /* Docs:
        * Summary
           * Saves in file a copy of this object as json.
        * Variables
            * FilePath - The path to save the file.
        */
        public void makePickle(string FilePath)
        {
            FileStream fs;
            fs = new FileStream(@FilePath, FileMode.Create, FileAccess.Write);
            StreamWriter writer = new StreamWriter(fs);
            writer.Write(sqHelpper.BeautifyJson(this.ToJson()));
            writer.Close();
            fs.Close();
        }

        /* Docs:
        * Summary: Checks if the results is empty.
        */
        public bool isEmpty()
        {
            if (this.Params.Count() == 0)
            {                
                return true;
            }
            else
                return false;
        }

        /* Docs:
        * Summary: Checks if the results is not empty.
        */
        public bool isNotEmpty()
        {
            if (this.isEmpty())
                return false;
            else
                return true;
        }

        /* Docs:
        * Summary: Returns the number of rows.
        */
        public int numOfRows()
        {
            return this.Params.Count();
        }

        /* Docs:
        * Summary: Returns the number of columns.
        */
        public int numOfColumns()
        {
            return this.Titles.Count();
        }

        /* Docs:
        * Summary: Returns the value of specific cell in the table.
        * Variables
            * RowNum - Index of the cell's row in the table.
            * ColumnNum - Index of the cell's column in the table.
        */
        public string getCellValue(int RowNum, int ColumnNum)
        {
            return (string)this.Params[RowNum][ColumnNum];
        }

        /* Docs:
        * Summary: Returns an entire line as a list.
        * Variables
            * RowNum - Index of the row in the table.            
        */
        public List<object> getRow(int RowNum)
        {
            return this.Params[RowNum];
        }

        /* Docs:
        * Summary: Returns an entire line as a list.
        * Variables
            * ColumnNum - Index of the column in the table.      
        */
        public List<object> getColumn(int ColumnNum)
        {
            List<object> col= new List<object>();
            for (int rownum = 0 ; rownum<this.Params.Count() ; ++rownum)
            {
                col.Add(this.Params[rownum][ColumnNum]);
            }
            return col;
        }

        /* Docs:
        * Summary: Checks if row equals to specific row in Params.
        * Variables
            * RowNum - Index of the specific row in Params.
            * Item - The row to compare to.
            * IgnoreColumnNums - List of indexes of columns we want to ignore in the comparing.
        */
        public bool isEquleRow(int RowNum, List<object> Row, List<int> IgnoreColumnNums= null)
        {
            bool b = true;
            if (IgnoreColumnNums == null) //Initialize the list if it not set
                IgnoreColumnNums = new List<int>();
                if (Params[RowNum].ToJson().Equals(Row.ToJson())) //Check if rows equals, using json.
                    return b;
                for (int tab = 0; tab < this.Params[RowNum].Count(); ++tab) //If needed to ignore columns, checks row by comparing each cell, except cells that should ignored.
            {
                if (IgnoreColumnNums.Contains(tab))
                    Console.WriteLine("ignored");
                else if (!this.Params[RowNum][tab].ToJson().Equals(Row[tab].ToJson()))
                {
                    b = false;
                }
            }
            return b;
        }

        /* Docs:
        * Summary: Compares this results to another result.
        * Variables
            * Result - Another result to compare to.
            * IgnoreColumnNums - List of indexes of columns we want to ignore in the comparing.
        */
        public sqQueryTableResult[] compareSqResult(sqQueryTableResult Result, List<int> IgnoreColumnNums = null)
        {
            if (IgnoreColumnNums == null)
                IgnoreColumnNums = new List<int>(); //Initialize the list if it not set
            List<List<object>> equles = new List<List<object>>();  
            List<List<object>> onlySelf = new List<List<object>>();
            List<List<object>> onlyResult =  new List<List<object>>();
            bool r = false;
            bool s = false;
            //Check for each row in the another result, if its have an equal row in this result.
            foreach( List<object> row in Result.Params )
            {
                for (int selfRow= 0 ; selfRow< this.Params.Count() ; ++selfRow )
                    if ( this.isEquleRow(selfRow,row,IgnoreColumnNums ))
                    {
                        equles.Add(row);
                        r = true;
                    }
                if (r == false)
                    onlyResult.Add(row);
                r = false;
            }
            //Check for each row in this result, if its have an equal row in the another result.
            for (int selfRow = 0; selfRow < this.Params.Count(); ++selfRow)
            {
                foreach (List<object> row in Result.Params)
                    if (this.isEquleRow(selfRow, row, IgnoreColumnNums))
                        s = true;
                if (!s)
                    onlySelf.Add(this.Params[selfRow]);
                s = false;
            }
            //Returns the new rows, the dropped rows, and the equals rows , between this result and the another result.
            return new sqQueryTableResult[3] { new sqQueryTableResult(this.Titles, equles), new sqQueryTableResult(this.Titles,onlySelf), new sqQueryTableResult(this.Titles,onlyResult)};
        }

        /* Docs:
        * Summary: Returns the names of table's headers.
        */
        public string[] getColumnsNames()
        {
            string[] cols = new string[this.Titles.Count()];
            for (int column = 0; column < cols.Length; ++column)
                cols[column] = this.Titles[column].ToString();
            return cols;
        }


    } //end of sqQueryTableResult Class
}
